package classifiers;

import weka.classifiers.trees.RandomForest;
import Utilities.Constants;
import data_structures.DiscreteHP;
import data_structures.ContinuousHP;
import data_structures.HPConfiguration;
import data_structures.HyperParameter;
import weka.classifiers.Evaluation;
import weka.classifiers.lazy.IBk;
import weka.core.Instances;

/**
 * Random Forest classifier using Weka's Random Forest classifier.
 */
public class RandomForestClassifier implements Classifier {
    
    private int noOfTreesLowerBound;
    private int noOfTreesUpperBound;
    private int noOfFeaturesToUseLowerBound;
    private int noOfFeaturesToUseUpperBound;

    public RandomForestClassifier(int noOfTreesLowerBound, int noOfTreesUpperBound, 
				  int noOfFeaturesToUseLowerBound, int noOfFeaturesToUseUpperBound) {
	this.noOfTreesLowerBound = noOfTreesLowerBound;
	this.noOfTreesUpperBound = noOfTreesUpperBound;
	this.noOfFeaturesToUseLowerBound = noOfFeaturesToUseLowerBound;
	this.noOfFeaturesToUseUpperBound = noOfFeaturesToUseUpperBound;
    }
    
    public double evaluateClassifier(Instances instances,
					 HPConfiguration hpConfiguration) {
	try {
	    int noOfTrees = (int)hpConfiguration.getHyperParameters()[0].getValue();
	    int noOfFeaturesToUse = (int)hpConfiguration.getHyperParameters()[1].getValue();

	    String[] options = new String[6];
	    options[0] = "-I";
	    options[1] = Integer.toString(noOfTrees);
	    options[2] = "-K";
	    options[3] = Integer.toString(noOfFeaturesToUse);
	    options[4] = "-S";
	    options[5] = "1";
 
	    RandomForest randomForest = new RandomForest();
	    randomForest.setOptions(options);
	
	    Evaluation eval = new Evaluation(instances);
	    eval.crossValidateModel(randomForest, instances, 10, 
				    Constants.RANDOM);
	    double accuracy = eval.pctCorrect();
	    return accuracy;
	} catch (Exception e) {
	    e.printStackTrace();
	}

	return -1;
    }

    public HPConfiguration getHPConfiguration() {
	HyperParameter[] hps = new HyperParameter[2];
	hps[0] = new DiscreteHP(noOfTreesLowerBound, noOfTreesUpperBound, 
				noOfTreesLowerBound);
	hps[1] = new DiscreteHP(noOfFeaturesToUseLowerBound, noOfFeaturesToUseUpperBound, 
				noOfFeaturesToUseLowerBound);
	return new HPConfiguration(hps);
    }
}
